import moment from 'moment';
import * as t from '../type';
import { DirContext } from './context';
import orgCtrl from '@/ts/controller';

/**
 * 生成一份 Excel 文件
 * @param excel 表格信息
 * @param filename 文件信息
 */
export const generateXlsx = (excel: t.IExcel, filename: string) => {
  try {
    let workbook = t.XLSX.utils.book_new();
    let sheets = excel.handlers.map((item) => item.sheet);
    for (let sheet of sheets) {
      let headers = sheet.columns.map((item) => item.title);

      let converted = [];
      for (let item of sheet.data) {
        let newItem: { [key: string]: any } = {};
        sheet.columns.forEach((column) => {
          newItem[column.title] = item[column.dataIndex];
        });
        converted.push(newItem);
      }

      let jsonSheet = t.XLSX.utils.json_to_sheet(converted, {
        header: headers,
        skipHeader: false,
      });
      t.XLSX.utils.book_append_sheet(workbook, jsonSheet, sheet.name);
    }
    t.XLSX.writeFileXLSX(workbook, filename + '.xlsx');
    return true;
  } catch (err) {
    return false;
  }
};

/**
 * 收集 Excel 数据
 */
export const readXlsx = (file: Blob, excel: t.IExcel): Promise<t.IExcel> => {
  return new Promise((resolve, reject) => {
    let reader = new FileReader();
    reader.onload = async (e) => {
      try {
        let workbook = t.XLSX.read(e.target?.result, { type: 'binary' });
        let keys = Object.keys(workbook.Sheets);

        for (let index = 0; index < keys.length; index++) {
          await collecting(keys[index], workbook.Sheets, excel);
        }

        resolve(excel);
      } catch (error) {
        reject(error);
      }
    };
    reader.readAsArrayBuffer(file);
  });
};

/**
 * 数据收集，将中文名称转换为英文名称
 */
export const collecting = async (
  key: string,
  reads: { [sheet: string]: t.XLSX.WorkSheet },
  excel: t.IExcel,
): Promise<void> => {
  const space = orgCtrl.user.companys.find((item) => item.id == excel.space.id);
  const personCache = new Map();
  const companyCache = new Map();
  const groupCache = new Map();
  const cohortCache = new Map();
  for (let sheet of excel.handlers.map((item) => item.sheet)) {
    if (sheet.name == key) {
      let ansData: any[] = [];
      let data: any[] = t.XLSX.utils.sheet_to_json(reads[key]);
      for (const item of data) {
        let ansItem: any = { labels: [] };
        for (const column of sheet.columns) {
          let value = item[column.title];
          if (value || value === 0) {
            switch (column.valueType) {
              case '选择型':
              case '分类型':
                if (column.lookups) {
                  const map: Map<String, t.model.FiledLookup> = new Map();
                  column.lookups.forEach((item) => map.set(item.id, item));
                  for (const item of column.lookups) {
                    if (item.text == value) {
                      ansItem[column.dataIndex] = item.value;
                      ansItem.labels.push(item.value);
                      let temp = item;
                      while (temp.parentId && map.has(temp.parentId)) {
                        const parent = map.get(temp.parentId)!;
                        ansItem.labels.push(parent.value);
                        temp = map.get(temp.parentId)!;
                      }
                      break;
                    }
                  }
                }
                break;
              case '日期型':
              case '时间型':
                let result: Date;
                switch (typeof value) {
                  case 'number':
                    const date = t.XLSX.SSF.parse_date_code(value);
                    result = new Date(date.y, date.m - 1, date.d, date.H, date.M, date.S);
                    break;
                  case 'object':
                    if (value instanceof Date) {
                      result = value;
                      break;
                    }
                    return;
                  case 'string':
                    try {
                      const converted = moment(value, true);
                      if (converted.isValid()) {
                        result = converted.toDate();
                        break;
                      }
                      continue;
                    } catch (e) {
                      continue;
                    }
                  default:
                    return;
                }
                if (column.valueType == '日期型') {
                  ansItem[column.dataIndex] = moment(result, 'YYYY-MM-DD');
                } else {
                  ansItem[column.dataIndex] = moment(result, 'YYYY-MM-DD HH:mm:ss');
                }
                break;
              case '数值型':
                ansItem[column.dataIndex] = Number(value);
                break;
              case '用户型':
                switch (column.widget ?? '人员搜索框') {
                  case '操作人':
                    ansItem[column.dataIndex] = excel.space.userId;
                    break;
                  case '操作组织':
                    ansItem[column.dataIndex] = excel.space.id;
                    break;
                  case '成员选择框':
                    const target = orgCtrl.targets.find(
                      (i) => i.id === column.options?.teamId,
                    );
                    const result = target?.members.find((item) => item.name == value);
                    if (result) {
                      ansItem[column.dataIndex] = result.id;
                    }
                    break;
                  case '内部机构选择框':
                    if (space) {
                      const result = [
                        ...space.cohorts,
                        ...space.departments,
                        ...space.stations,
                      ];
                      const find = result.find((item) => item.name == value);
                      if (find) {
                        ansItem[column.dataIndex] = find.id;
                      }
                    }
                    break;
                  case '人员搜索框': {
                    const types = [t.TargetType.Person];
                    const result = await loadTargetId(personCache, types, value);
                    if (result) {
                      ansItem[column.dataIndex] = result;
                    }
                    break;
                  }
                  case '单位搜索框': {
                    const types = [t.TargetType.Company];
                    const result = await loadTargetId(companyCache, types, value);
                    if (result) {
                      ansItem[column.dataIndex] = result;
                    }
                    break;
                  }
                  case '组织群搜索框': {
                    const types = [t.TargetType.Group];
                    const result = await loadTargetId(groupCache, types, value);
                    if (result) {
                      ansItem[column.dataIndex] = result;
                    }
                    break;
                  }
                  case '群组搜索框': {
                    const types = [t.TargetType.Cohort];
                    const result = await loadTargetId(cohortCache, types, value);
                    if (result) {
                      ansItem[column.dataIndex] = result;
                    }
                    break;
                  }
                }
                break;
            }
          }
        }
        ansData.push(ansItem);
      }
      sheet.data = ansData;
    }
  }
};

const loadTargetId = async (
  cache: Map<string, t.schema.XTarget | undefined>,
  types: t.TargetType[],
  value: string,
): Promise<string | undefined> => {
  if (!cache.has(value)) {
    const target = await orgCtrl.user.searchTargets(value.toString(), types);
    if (target.length > 0) {
      cache.set(value, target[0]);
    } else {
      cache.set(value, undefined);
    }
  }
  return cache.get(value)?.id;
};

export class Excel implements t.IExcel {
  handlers: t.ISheetHandler<t.model.Sheet<any>>[];
  dataHandler?: t.DataHandler;
  context: t.Context;
  space: t.IBelong;

  constructor(
    space: t.IBelong,
    sheets?: t.ISheetHandler<t.model.Sheet<any>>[],
    handler?: t.DataHandler,
  ) {
    this.space = space;
    this.handlers = [];
    this.dataHandler = handler;
    this.context = new DirContext();
    sheets?.forEach((item) => this.appendHandler(item));
  }

  getHandler(name: string) {
    return this.handlers.filter((item) => item.sheet.name == name)[0];
  }

  appendHandler(handler: t.ISheetHandler<any>): void {
    const judge = (item: any) => item.sheet.name == handler.sheet.name;
    const index = this.handlers.findIndex(judge);
    if (index == -1) {
      this.handlers.push(handler);
    } else {
      this.handlers[index] = handler;
    }
  }

  async handling(): Promise<void> {
    try {
      let totalRows = this.handlers
        .map((item) => item.sheet)
        .map((item) => item.data.length)
        .reduce((f, s) => f + s);
      this.dataHandler?.initialize?.(totalRows);

      for (const handler of this.handlers) {
        await handler.operating(this, () => this.dataHandler?.onItemCompleted?.());
        handler.completed?.(this);
      }
      this.dataHandler?.onCompleted?.();
    } catch (error: any) {
      this.dataHandler?.onError?.('数据处理异常');
    }
  }
}
